package com.wct.demo.gui;

import com.wct.demo.BankApp;
import com.wct.demo.BankTask;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author WEI (Email:changtan20000@163.com)
 * @description
 * @date: 2019/12/22 12:33
 */
@Slf4j
public class BankGUI {

    // 窗口文本
    private JTextArea textArea1;
    private JTextArea textArea2;
    private JTextArea textArea3;
    private JTextArea textArea4;
    private JLabel topLabel;

    private JTextArea windowsText1;
    private JTextArea windowsText2;
    private JTextArea windowsText3;
    private JTextArea windowsText4;

    private JTextArea taskText;


    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final String[] WINDOWS_NAMES = {"Windows-1", "Windows-2", "Windows-3", "Windows-4"};
    private static final String[] WINDOWS_THREAD_NAMES = {"pool-1-thread-1", "pool-1-thread-2", "pool-1-thread-3", "pool-1-thread-4"};

    /**
     * 创建银行界面
     */
    public void createAndShowGUI() {
        JPanel windowsPane = createWindowsPanel();
        JPanel bankPane = createBankPanel();

        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        tabbedPane.addTab("windows", windowsPane);
        tabbedPane.addTab("bank", bankPane);

        //Create and set up the window.
        JFrame frame = new JFrame("BakGUI");
        // 设置当窗体关闭的操作时，程序结束
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setContentPane(tabbedPane);
        /* 设置窗口大小
        setSize,setLocation,setBounds方法需要在不使用布局管理器的时候使用，
        也就是setLayout(null)的时候可以使用这三个方法控制布局*/
        // setPreferredSize需要在使用布局管理器的时候使用
        frame.setPreferredSize(new Dimension(900, 600));

        // 设置窗体可显示
        frame.pack();
        frame.setVisible(true);
        // 设置窗体居中显示
        frame.setLocationRelativeTo(null);

        // 时间显示
        Timer timer = new Timer(1000, e -> {
            // 获取到当前时间并按格式显示
            if (BankApp.getIsOpen()) {
                topLabel.setText("正在营业\nTime: " + LocalDateTime.now().format(DATE_TIME_FORMATTER));
            } else {
                topLabel.setText("停止营业\nTime: " + LocalDateTime.now().format(DATE_TIME_FORMATTER));
            }
        });
        timer.start();
    }

    /**
     * @return 创建银行面板
     */
    private JPanel createBankPanel() {
        // 边距
        Border border = BorderFactory.createEmptyBorder(5, 10, 10, 10);

        GridLayout windowsLayout = new GridLayout(1, 1, 10, 10);

        JPanel panel1 = new JPanel(windowsLayout);
        panel1.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[0]),
                border));
        windowsText1 = new MyTextArea();
        panel1.add(windowsText1);

        JPanel panel2 = new JPanel(windowsLayout);
        panel2.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[1]),
                border));
        windowsText2 = new MyTextArea();
        panel2.add(windowsText2);

        JPanel panel3 = new JPanel(windowsLayout);
        panel3.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[2]),
                border));
        windowsText3 = new MyTextArea();
        panel3.add(windowsText3);

        JPanel panel4 = new JPanel(windowsLayout);
        panel4.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[3]),
                border));
        windowsText4 = new MyTextArea();
        panel4.add(windowsText4);

        JPanel windowsPane = new JPanel();
        windowsPane.setBorder(border);
        windowsPane.setLayout(new GridLayout(1, 4, 10, 10));

        windowsPane.add(panel1);
        windowsPane.add(panel2);
        windowsPane.add(panel3);
        windowsPane.add(panel4);

        //Create a text area.
        taskText = new MyTextArea();
        JPanel taskPanel = new JPanel(windowsLayout);
        taskPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Bank task list"),
                BorderFactory.createEmptyBorder(10, 10, 10, 10)));
        taskPanel.add(new JScrollPane(taskText));

        JPanel panel = new JPanel();
        panel.setBorder(border);
        panel.setLayout(new GridLayout(2, 1, 10, 10));
        panel.add(windowsPane);
        panel.add(taskPanel);

        return panel;
    }

    /**
     * @return 创建窗口面板
     */
    private JPanel createWindowsPanel() {
        // 边距
        Border border = BorderFactory.createEmptyBorder(5, 10, 5, 10);

        JPanel topPanel = new JPanel();
        topPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Bank Windows"),
                border));
        topLabel = new JLabel("Hello Bank!");
        topLabel.setFont(new Font(null, Font.PLAIN, 20));
        topPanel.add(topLabel);

        GridLayout gridLayout = new GridLayout(1, 2, 10, 10);
        gridLayout.setHgap(10);
        gridLayout.setVgap(10);

        JPanel panel1 = new JPanel(gridLayout);
        panel1.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[0]),
                border));
        textArea1 = new MyTextArea();
        textArea1.setName("pool-1-thread-1");
        // 把textArea1放到滚动面板中
        panel1.add(new JScrollPane(textArea1));

        JPanel panel2 = new JPanel(gridLayout);
        panel2.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[1]),
                border));
        textArea2 = new MyTextArea();
        textArea2.setName("pool-1-thread-2");
        panel2.add(new JScrollPane(textArea2));

        JPanel panel3 = new JPanel(gridLayout);
        panel3.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[2]),
                border));
        textArea3 = new MyTextArea();
        textArea3.setName("pool-1-thread-3");
        panel3.add(new JScrollPane(textArea3));

        JPanel panel4 = new JPanel(gridLayout);
        panel4.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(WINDOWS_NAMES[3]),
                border));
        textArea4 = new MyTextArea();
        textArea4.setName("pool-1-thread-4");
        panel4.add(new JScrollPane(textArea4));

        JPanel panel = new JPanel(new GridLayout(5, 1));

        panel.add(topPanel);
        panel.add(panel1);
        panel.add(panel2);
        panel.add(panel3);
        panel.add(panel4);

        return panel;
    }

    /**
     * 更新银行面板的信息
     */
    public void updateBankPanel(java.util.List<BankTask> taskList) {
        // 所有业务的等待时间
        AtomicLong sumWaitTime = new AtomicLong(0);
        final int taskSize = taskList.size();
        taskText.setText("");
        windowsText1.setText("");
        windowsText2.setText("");
        windowsText3.setText("");
        windowsText4.setText("");
        SwingWorker<ConcurrentMap<String, Integer>, BankTask> worker = new SwingWorker<ConcurrentMap<String, Integer>, BankTask>() {
            @Override
            protected ConcurrentMap<String, Integer> doInBackground() {
                ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
                for (BankTask task : taskList) {
                    publish(task);
                    if (map.containsKey(task.getProcessWindows())) {
                        // 窗口办理人数加1
                        map.put(task.getProcessWindows(), map.get(task.getProcessWindows()) + 1);

                    } else {
                        map.put(task.getProcessWindows(), 1);
                    }
                    if (map.containsKey(task.getProcessWindows() + "-" + task.getType())) {
                        // 窗口业务办理人数加1
                        map.put(task.getProcessWindows() + "-" + task.getType(), map.get(task.getProcessWindows() + "-" + task.getType()) + 1);
                    } else {
                        map.put(task.getProcessWindows() + "-" + task.getType(), 1);
                    }
                    sumWaitTime.addAndGet(Duration.between(task.getWaitStartTime(), task.getProcessEndTime()).getSeconds());
                }
                return map;
            }

            @Override
            protected void process(java.util.List<BankTask> chunks) {
                for (BankTask task : chunks) {
                    log.debug("updateBankPanel process : {}", task.toString());
                    taskText.append(task.toString() + "\n");
                }
            }

            @Override
            protected void done() {
                try {
                    if (taskSize != 0) {
                        log.debug("updateBankPanel done avgWaitTime: {}", sumWaitTime.get() / taskSize);
                    }
                    ConcurrentMap<String, Integer> map;
                    map = get();
                    map.forEach((k, v) -> {
                        if (k.contains(WINDOWS_THREAD_NAMES[0])) {
                            windowsText1.append("k: " + k + ", v: " + v + "\n");
                        }
                        if (k.contains(WINDOWS_THREAD_NAMES[1])) {
                            windowsText2.append("k: " + k + ", v: " + v + "\n");
                        }
                        if (k.contains(WINDOWS_THREAD_NAMES[2])) {
                            windowsText3.append("k: " + k + ", v: " + v + "\n");
                        }
                        if (k.contains(WINDOWS_THREAD_NAMES[3])) {
                            windowsText4.append("k: " + k + ", v: " + v + "\n");
                        }
                        log.debug("updateBankPanel done k: {}, v: {}", k, v);
                    });
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        worker.execute();
    }

    /**
     * 清除窗口的业务办理信息
     */
    public synchronized void clearWindowsText() {
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() {
                return null;
            }

            @Override
            protected void done() {
                textArea1.setText("");
                textArea2.setText("");
                textArea3.setText("");
                textArea4.setText("");
            }
        };
        worker.execute();
    }

    /**
     * 添加窗口处理的业务
     *
     * @param name  窗口
     * @param value 任务信息
     */
    public synchronized void appendWindowsTask(String name, String value) {
        SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
            @Override
            protected String doInBackground() {
                return value;
            }

            @Override
            protected void done() {
                try {
                    if (WINDOWS_THREAD_NAMES[0].equals(name)) {
                        textArea1.append(get() + "\n");
                    }
                    if (WINDOWS_THREAD_NAMES[1].equals(name)) {
                        textArea2.append(get() + "\n");
                    }
                    if (WINDOWS_THREAD_NAMES[2].equals(name)) {
                        textArea3.append(get() + "\n");
                    }
                    if (WINDOWS_THREAD_NAMES[3].equals(name)) {
                        textArea4.append(get() + "\n");
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };
        worker.execute();
    }

    public static void main(String[] args) throws Exception {
        BankGUI bankGUI = new BankGUI();
        // 执行bankGUI的createAndShowGUI方法
        SwingUtilities.invokeLater(bankGUI::createAndShowGUI);
        Thread.sleep(2000);
    }
}